#pragma once

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

#ifndef __static_inline
#define __static_inline static inline
#endif

typedef struct ringbuffer_struct *ringbuffer_t;

__static_inline ringbuffer_t ringbuffer_init(void *buffer, size_t buf_size);
__static_inline void ringbuffer_clr(ringbuffer_t handler);

__static_inline size_t ringbuffer_poll_write(ringbuffer_t handler, uint8_t data);
__static_inline size_t ringbuffer_fifo_fill(ringbuffer_t handler, const void *data, size_t size);
__static_inline size_t ringbuffer_poll_read(ringbuffer_t handler, uint8_t *data);
__static_inline size_t ringbuffer_fifo_read(ringbuffer_t handler, void *data, size_t size);

__static_inline bool ringbuffer_is_ne(ringbuffer_t handler);
__static_inline size_t ringbuffer_get_valid_size(ringbuffer_t handler);
__static_inline size_t ringbuffer_get_empty_size(ringbuffer_t handler);

__static_inline void ringbuffer_peek_valid(ringbuffer_t handler, void **dst_base, size_t *dst_size);
__static_inline void ringbuffer_peek_empty(ringbuffer_t handler, void **dst_base, size_t *dst_size);

/* ******************************************************************************************************************** */

typedef struct
{
    size_t size; // 缓存总大小
    size_t wid;  // 已写进的下标
    size_t rid;  // 已读出的下标
    uint8_t data[0];
} __ringbuf_t;

/**
 * @brief 初始化环形缓存
 *
 * @param handler   初始化环形缓存对象
 * @param pipe_size 可保存的最大长度
 * @return 缓存句柄
 */
__static_inline ringbuffer_t ringbuffer_init(void *buffer, size_t buf_size)
{
    __ringbuf_t *hdl = (__ringbuf_t *)buffer;
    hdl->size = buf_size - sizeof(__ringbuf_t) - 1;
    ringbuffer_clr((ringbuffer_t)hdl);
    return (ringbuffer_t)hdl;
}

/**
 * @brief 清空环形缓存的数据
 *
 * @param handler 由 ringbuffer_init() 初始化
 */
__static_inline void ringbuffer_clr(ringbuffer_t handler)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;
    hdl->wid = 0;
    hdl->rid = 0;
}

/**
 * @brief 写一个字节到缓存中（写入缓存）
 *
 * @param handler 由 ringbuffer_init() 初始化
 * @param data 值（字节）
 * @return size_t 返回实际复制成功的字节数
 */
__static_inline size_t ringbuffer_poll_write(ringbuffer_t handler, uint8_t data)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;
    size_t wid;

    wid = hdl->wid;
    if (++wid >= hdl->size)
    {
        wid = 0;
    }

    if (wid != hdl->rid)
    {
        ((uint8_t *)&hdl[1])[hdl->wid] = data;

        hdl->wid = wid;

        return 1;
    }

    return 0;
}

/**
 * @brief 把内存数据复制到缓存中
 *
 * @param handler 由 ringbuffer_init() 初始化
 * @param data 来源数据。如果值为 NULL 时，仅执行填充
 * @param size 来源数据大小（字节）
 * @return size_t 返回实际复制成功的字节数
 */
__static_inline size_t ringbuffer_fifo_fill(ringbuffer_t handler, const void *data, size_t size)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;
    size_t ret = 0;
    size_t wid;

    wid = hdl->wid;

    while (size)
    {
        size_t rid = *(volatile size_t *)&hdl->rid;
        size_t min;
        size_t remain;

        if (wid >= rid)
        {
            remain = hdl->size - wid - (rid == 0);
        }
        else
        {
            remain = rid - wid - 1;
        }
        if (remain == 0)
        {
            break;
        }
        min = size < remain ? size : remain;

        if (data)
        {
            memcpy(&((uint8_t *)&hdl[1])[wid], &((uint8_t *)data)[ret], min);
        }

        if ((wid += min) >= hdl->size)
        {
            wid = 0;
        }

        size -= min;
        ret += min;
    }

    hdl->wid = wid;

    return ret;
}

/**
 * @brief 从缓存复制一个字节到指定地址中
 *
 * @param handler 由 ringbuffer_init() 初始化
 * @param data 目标内存地址
 * @return size_t 返回 0 表示缓存空
 */
__static_inline size_t ringbuffer_poll_read(ringbuffer_t handler, uint8_t *data)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;
    size_t rid;

    rid = hdl->rid;
    if (rid != hdl->wid)
    {
        data[0] = ((uint8_t *)&hdl[1])[rid++];
        if (rid < hdl->size)
        {
            hdl->rid = rid;
        }
        else
        {
            hdl->rid = 0;
        }
        return 1;
    }

    return 0;
}

/**
 * @brief 从环形缓存中复制数据到内存（从缓存读取）。注：参数 data 值可以为 NULL，此时不复制数据，只释放相应的数据量
 *
 * @param handler 由 ringbuffer_init() 初始化
 * @param data 目标内存地址。如果值为 NULL 时，仅执行释放
 * @param size 目标内存大小（字节）
 * @return size_t 返回实际复制成功的字节数
 */
__static_inline size_t ringbuffer_fifo_read(ringbuffer_t handler, void *data, size_t size)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;
    size_t ret = 0;
    size_t rid;

    rid = hdl->rid;
    while (size)
    {
        size_t wid = *(volatile size_t *)&hdl->wid;
        size_t min;
        size_t remain;

        if (rid > wid)
        {
            remain = hdl->size - rid;
        }
        else
        {
            remain = wid - rid;
        }
        if (remain == 0)
        {
            break;
        }
        min = size < remain ? size : remain;

        if (data)
        {
            memcpy(&((uint8_t *)data)[ret], &((uint8_t *)&hdl[1])[rid], min);
        }

        if ((rid += min) >= hdl->size)
        {
            rid = 0;
        }

        size -= min;
        ret += min;
    }
    hdl->rid = rid;

    return ret;
}

/**
 * @brief 获取环形缓存非空
 *
 * @param handler 由 ringbuffer_init() 初始化
 * @retval true 有数据
 * @retval false 表示空
 */
__static_inline bool ringbuffer_is_ne(ringbuffer_t handler)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;
    return (bool)(hdl->rid != hdl->wid);
}

/**
 * @brief 获取环形缓存的数据大小（字节数）
 *
 * @param handler 由 ringbuffer_init() 初始化
 * @return size_t 数据大小（字节数）
 */
__static_inline size_t ringbuffer_get_valid_size(ringbuffer_t handler)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;

    if (hdl->wid < hdl->rid)
    {
        return hdl->size + hdl->wid - hdl->rid;
    }
    else
    {
        return hdl->wid - hdl->rid;
    }
}

/**
 * @brief 获取环形缓存的剩余空间（字节数）
 *
 * @param handler 由 ringbuffer_init() 初始化
 * @return size_t 总剩余大小（字节数）
 */
__static_inline size_t ringbuffer_get_empty_size(ringbuffer_t handler)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;
    return hdl->size - (ringbuffer_get_valid_size(handler) + 1);
}

/**
 * @brief 获取当前已写入的连续的内存信息。
 * hdl 实际是一环形缓存结构，获取的 dst_size 是指在缓存中的连续的长度，因此并不一定等于总长度。
 * 当需要读取已缓存数据时，取得当前已缓存的数据的连续内存信息后，可对缓存的直接访问，以节省多次复制内存的资源开销，
 * 配合使用 ringbuffer_fifo_read(handler, NULL, size) 直接释放对应的长度。
 *
 * @param handler   由 ringbuffer_init() 初始化
 * @param dst_base[out] 已缓存数据的内存地址
 * @param dst_size[out] 已缓存数据的连续长度（字节）
 */
__static_inline void ringbuffer_peek_valid(ringbuffer_t handler, void **dst_base, size_t *dst_size)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;

    if (dst_base)
    {
        *dst_base = (&((uint8_t *)&hdl[1])[hdl->rid]);
    }
    if (dst_size)
    {
        size_t rid = hdl->rid;
        size_t wid = hdl->wid;
        if (rid > wid)
        {
            *dst_size = hdl->size - rid;
        }
        else
        {
            *dst_size = wid - rid;
        }
    }
}

/**
 * @brief 获取当前空闲的连续的内存信息。
 * hdl 实际是一环形缓存结构，获取的 dst_size 是指在缓存中的连续的长度，因此并不一定等于总长度。
 * 当需要写入缓存数据时，此函数用于取得当前空闲的连续内存信息后，可对缓存的直接访问，以节省多次复制内存的资源开销，
 * 配合使用 ringbuffer_fifo_fill(handler, NULL, size) 直接填充对应的长度。
 *
 * @param handler   由 ringbuffer_init() 初始化
 * @param dst_base[out] 当前空闲的内存地址
 * @param dst_size[out] 当前空闲的连续长度（字节）
 */
__static_inline void ringbuffer_peek_empty(ringbuffer_t handler, void **dst_base, size_t *dst_size)
{
    __ringbuf_t *hdl = (__ringbuf_t *)handler;

    if (dst_base)
    {
        *dst_base = (&((uint8_t *)&hdl[1])[hdl->wid]);
    }
    if (dst_size)
    {
        size_t rid = hdl->rid;
        size_t wid = hdl->wid;
        if (wid >= rid)
        {
            *dst_size = hdl->size - wid - (rid == 0);
        }
        else
        {
            *dst_size = rid - wid - 1;
        }
    }
}
